package com.papper.common.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.papper.common.bean.BaseEntity;
import com.papper.common.cache.CacheConstants;
import com.papper.common.cache.redis.CacheClosureNullDefault;
import com.papper.common.cache.redis.RedisCache;
import com.papper.common.mapper.PapperMapper;
import com.papper.common.utils.ClassUtil;
import com.papper.common.utils.tool.StringTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.util.StringUtil;

import java.io.Serializable;
import java.util.List;

/**
 * 基础服务类
 *
 * @param <T>
 * @author lajiao
 */
public class ServiceImpl<M extends PapperMapper<T>, T> {
    protected static Logger logger = LoggerFactory.getLogger(ServiceImpl.class);

    @Autowired
    protected RedisCache cache;
    @Autowired(required = false)
    protected M          papperMapper;

    public M getPapperMapper() {
        return this.papperMapper;
    }

    public String getModel() {
        Service serviceProperty = this.getClass().getAnnotation(Service.class);
        return serviceProperty.value();
    }

    protected void clearCaches(T entity, T old) {
        if (entity != null) {
            clearCaches(entity);
        }
        if (old != null) {
            clearCaches(old);
        }
    }

    protected void clearCaches(T entity) {
        Service serviceProperty = this.getClass().getAnnotation(Service.class);
        // 主键缓存
        BaseEntity base = (BaseEntity) entity;
        cache.delete(CacheConstants.Keys(serviceProperty.value(), "ID", base.id()));
        // 全局缓存
        cache.delete(CacheConstants.Keys(serviceProperty.value(), "ALL"));
    }

    /**
     * insert
     */
    public int save(T entity) {
        if (entity == null) {
            logger.error("保存的实体不可以为空");
            return 0;
        }
        int i = getPapperMapper().insert(entity);
        clearCaches(entity);
        return i;
    }

    public void batchInsert(List<T> entitys) {
        if (entitys == null || entitys.isEmpty()) {
            return;
        }
        getPapperMapper().insertList(entitys);
        for (T entity : entitys) {
            clearCaches(entity);
        }
    }

    /**
     * update
     */
    public int update(T entity) {
        if (entity == null) {
            logger.error("更新的实体不可以为空");
            return 0;
        }
        BaseEntity base = (BaseEntity) entity;
        if (base.id() == null) {
            logger.error("更新的实体主键不可以为空,[{}]", getPapperMapper().getClass());
            return 0;
        }
        T old = findById(base.id());
        if (old == null) {
            logger.error("更新的实体数据库不存在:[{}]", getPapperMapper().getClass());
            return 0;
        }
        int i = getPapperMapper().updateByPrimaryKeySelective(entity);
        if (i > 0) {
            clearCaches(entity, old);
        }
        return i;
    }

    public int updateNoLog(T entity) {
        if (entity == null) {
            logger.error("更新的实体不可以为空");
            return 0;
        }
        BaseEntity base = (BaseEntity) entity;
        if (base.id() == null) {
            logger.error("更新的实体主键不可以为空,[{}]", getPapperMapper().getClass());
            return 0;
        }
        T old = findById(base.id());
        if (old == null) {
            logger.error("更新的实体数据库不存在:[{}]", getPapperMapper().getClass());
            return 0;
        }
        int i = getPapperMapper().updateByPrimaryKeySelective(entity);
        if (i > 0) {
            clearCaches(entity, old);
        }
        return i;
    }

    public int saveOrUpdate(T entity) {
        if (entity == null) {
            logger.error("实体不可以为空");
            return 0;
        }
        BaseEntity base = (BaseEntity) entity;
        if (base.id() == null) {
            return save(entity);
        } else {
            return update(entity);
        }
    }

    /**
     * delete
     */
    public int deleteById(Serializable id) {
        if (id == null) {
            logger.error("删除的主键不可以为空:[{}]", getPapperMapper().getClass());
            return 0;
        }
        T entity = findById(id);
        if (entity != null) {
            return delete(entity);
        } else {
            return 0;
        }
    }

    public int delete(T entity) {
        if (entity == null) {
            logger.error("删除的实体不可以为空");
            return 0;
        }
        BaseEntity base = (BaseEntity) entity;
        if (base.id() == null) {
            logger.error("删除的实体主键不可以为空:[{}]", getPapperMapper().getClass());
            return 0;
        }
        int i = getPapperMapper().delete(entity);
        clearCaches(entity);
        return i;
    }

    public int delete(Example example) {
        List<T> lst = find(example);
        if (lst == null || lst.isEmpty()) {
            return 0;
        }
        int i = getPapperMapper().deleteByExample(example);
        if (i > 0) {
            for (T entity : lst) {
                clearCaches(entity);
            }
        }
        return i;
    }

    public int deleteNoCache(Example example) {
        int i = getPapperMapper().deleteByExample(example);
        return i;
    }

    /**
     * query
     */
    public T findById(Serializable id) {
        if (id == null) {
            return null;
        }
        if (StringTool.parseLong(id, 0L) <= 0) {
            return null;
        }
        T              rtn             = null;
        Service        serviceProperty = this.getClass().getAnnotation(Service.class);
        final Class<T> entityClass     = (Class<T>) ClassUtil.getSuperClassGenricType(this.getClass(), 1);
        if (serviceProperty != null) {
            final String key = CacheConstants.Keys(serviceProperty.value(), "ID", id);
            return cache.getAndSet(new CacheClosureNullDefault<T>() {
                @Override
                public String getKey() {
                    return key;
                }

                @Override
                public T getValue() {
                    return getPapperMapper().selectByPrimaryKey(id);
                }

                @Override
                public Class<T> getTypeReference() {
                    return entityClass;
                }
            });
        } else {
            return getPapperMapper().selectByPrimaryKey(id);
        }
    }

    public List<T> findByIds(String ids) {
        if (StringTool.isEmpty(ids)) {
            return null;
        }
        return getPapperMapper().selectByIds(ids);
    }

    public List<T> find(Example example) {
        if (StringUtil.isEmpty(example.getOrderByClause())) {
            example.setOrderByClause("id desc");
        }
        return getPapperMapper().selectByExample(example);
    }

    public List<T> findAll() {
        Service        serviceProperty = this.getClass().getAnnotation(Service.class);
        if (serviceProperty != null) {
            final Class<T> entityClass = (Class<T>) ClassUtil.getSuperClassGenricType(this.getClass(), 1);
            final String   key         = CacheConstants.Keys(serviceProperty.value(), "ALL");
            return cache.getAndSetList(new CacheClosureNullDefault<T>() {
                @Override
                public String getKey() {
                    return key;
                }

                @Override
                public List<T> getValue() {
                    return getPapperMapper().selectAll();
                }

                @Override
                public Class<T> getTypeReference() {
                    return entityClass;
                }
            });
        } else {
            return getPapperMapper().selectAll();
        }
    }

    public T findOne(Example example) {
        if (StringUtil.isEmpty(example.getOrderByClause())) {
            example.setOrderByClause("id desc limit 1");
        }
        return getPapperMapper().selectOneByExample(example);
    }

    public int count(Example example) {
        return getPapperMapper().selectCountByExample(example);
    }

    public PageInfo<T> findPage(int pageNum, int size, Example example) {
        if (StringUtil.isEmpty(example.getOrderByClause())) {
            example.setOrderByClause("id desc");
        }
        if (size > 10000) {
            size = 10000;
        }
        PageHelper.startPage(pageNum, size);
        return new PageInfo<T>(getPapperMapper().selectByExample(example));
    }
}
